package org.asblog.transform 
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.EventPhase;
	import flash.events.MouseEvent;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.Dictionary;
	
	import mx.core.UIComponent;
	
	import org.asblog.core.IMediaObject;		

	/**
	 * Creates a transform tool that allows uaers to modify display objects on the screen
	 * 
	 * @usage
	 * <pre>
	 * var tool:TransformTool = new TransformTool();
	 * addChild(tool);
	 * tool.target = targetDisplayObject;
	 * </pre>
	 * 
	 * @version 0.9.10
	 * @author  Trevor McCauley
	 * @author  http://www.senocular.com
	 */
	public class TransformTool extends UIComponent 
	{

		// Variables
		private var toolInvertedMatrix : Matrix = new Matrix( );
		private var innerRegistration : Point = new Point( );
		private var registrationLog : Dictionary = new Dictionary( true );

		private var targetBounds : Rectangle = new Rectangle( );

		private var mouseLoc : Point = new Point( );
		private var mouseOffset : Point = new Point( );
		private var innerMouseLoc : Point = new Point( );
		private var interactionStart : Point = new Point( );
		private var innerInteractionStart : Point = new Point( );
		private var interactionStartAngle : Number = 0;
		private var interactionStartMatrix : Matrix = new Matrix( );

		private var toolSprites : Sprite = new Sprite( );
		private var lines : Sprite = new Sprite( );
		private var moveControls : Sprite = new Sprite( );
		private var registrationControls : Sprite = new Sprite( );
		private var rotateControls : Sprite = new Sprite( );
		private var scaleControls : Sprite = new Sprite( );
		private var skewControls : Sprite = new Sprite( );
		private var cursors : Sprite = new Sprite( );
		private var customControls : Sprite = new Sprite( );
		private var customCursors : Sprite = new Sprite( );

		// With getter/setters
		private var _target : IMediaObject;
		private var _toolMatrix : Matrix = new Matrix( );
		//private var _globalMatrix:Matrix = new Matrix();

		private var _registration : Point = new Point( );

		
		private var _livePreview : Boolean = true;
		private var _raiseNewTargets : Boolean = true;
		private var _moveNewTargets : Boolean = false;
		private var _moveEnabled : Boolean = true;
		private var _registrationEnabled : Boolean = true;
		private var _rotationEnabled : Boolean = true;
		private var _scaleEnabled : Boolean = true; 
		private var _skewEnabled : Boolean = true;
		private var _outlineEnabled : Boolean = true;
		private var _customControlsEnabled : Boolean = true;
		private var _customCursorsEnabled : Boolean = true;
		private var _cursorsEnabled : Boolean = true; 
		private var _rememberRegistration : Boolean = true;

		private var _scale9GridEnabled : Boolean = false;

		
		private var _constrainScale : Boolean = false;
		private var _constrainRotationAngle : Number = Math.PI / 4; 
		// default at 45 degrees
		private var _constrainRotation : Boolean = false;

		private var _moveUnderObjects : Boolean = true;
		private var _maintainControlForm : Boolean = true;
		private var _controlSize : Number = 8;

		private var _maxScaleX : Number = Infinity;
		private var _maxScaleY : Number = Infinity;

		private var _boundsTopLeft : Point = new Point( );
		private var _boundsTop : Point = new Point( );
		private var _boundsTopRight : Point = new Point( );
		private var _boundsRight : Point = new Point( );
		private var _boundsBottomRight : Point = new Point( );
		private var _boundsBottom : Point = new Point( );
		private var _boundsBottomLeft : Point = new Point( );
		private var _boundsLeft : Point = new Point( );
		private var _boundsCenter : Point = new Point( );

		private var _currentControl : TransformToolControl;

		private var _moveControl : TransformToolControl;
		private var _registrationControl : TransformToolControl;
		private var _outlineControl : TransformToolControl;
		private var _scaleTopLeftControl : TransformToolControl;
		private var _scaleTopControl : TransformToolControl;
		private var _scaleTopRightControl : TransformToolControl;
		private var _scaleRightControl : TransformToolControl;
		private var _scaleBottomRightControl : TransformToolControl;
		private var _scaleBottomControl : TransformToolControl;
		private var _scaleBottomLeftControl : TransformToolControl;
		private var _scaleLeftControl : TransformToolControl;
		private var _rotationTopLeftControl : TransformToolControl;
		private var _rotationTopRightControl : TransformToolControl;
		private var _rotationBottomRightControl : TransformToolControl;
		private var _rotationBottomLeftControl : TransformToolControl;
		private var _skewTopControl : TransformToolControl;
		private var _skewRightControl : TransformToolControl;
		private var _skewBottomControl : TransformToolControl;
		private var _skewLeftControl : TransformToolControl;

		private var _moveCursor : TransformToolCursor;
		private var _registrationCursor : TransformToolCursor;
		private var _rotationCursor : TransformToolCursor;
		private var _scaleCursor : TransformToolCursor;
		private var _skewCursor : TransformToolCursor;

		// Event constants
		public static const NEW_TARGET : String = "newTarget";
		public static const TRANSFORM_TARGET : String = "transformTarget";
		public static const TRANSFORM_TOOL : String = "transformTool";
		public static const CONTROL_INIT : String = "controlInit";
		public static const CONTROL_TRANSFORM_TOOL : String = "controlTransformTool";
		public static const CONTROL_DOWN : String = "controlDown";
		public static const CONTROL_MOVE : String = "controlMove";
		public static const CONTROL_UP : String = "controlUp";
		public static const CONTROL_PREFERENCE : String = "controlPreference";

		// Skin constants
		public static const REGISTRATION : String = "registration";
		public static const SCALE_TOP_LEFT : String = "scaleTopLeft";
		public static const SCALE_TOP : String = "scaleTop";
		public static const SCALE_TOP_RIGHT : String = "scaleTopRight";
		public static const SCALE_RIGHT : String = "scaleRight";
		public static const SCALE_BOTTOM_RIGHT : String = "scaleBottomRight";
		public static const SCALE_BOTTOM : String = "scaleBottom";
		public static const SCALE_BOTTOM_LEFT : String = "scaleBottomLeft";
		public static const SCALE_LEFT : String = "scaleLeft";
		public static const ROTATION_TOP_LEFT : String = "rotationTopLeft";
		public static const ROTATION_TOP_RIGHT : String = "rotationTopRight";
		public static const ROTATION_BOTTOM_RIGHT : String = "rotationBottomRight";
		public static const ROTATION_BOTTOM_LEFT : String = "rotationBottomLeft";
		public static const SKEW_TOP : String = "skewTop";
		public static const SKEW_RIGHT : String = "skewRight";
		public static const SKEW_BOTTOM : String = "skewBottom";
		public static const SKEW_LEFT : String = "skewLeft";
		public static const CURSOR_REGISTRATION : String = "cursorRegistration";
		public static const CURSOR_MOVE : String = "cursorMove";
		public static const CURSOR_SCALE : String = "cursorScale";
		public static const CURSOR_ROTATION : String = "cursorRotate";
		public static const CURSOR_SKEW : String = "cursorSkew";

		public var outOfBorderline : Function;

		
		private var _originalWidth : Number;		private var _originalHeight : Number;
		private var isInteractioned : Boolean;
		private var oldToolMatrix : Matrix;

		// Properties
		/**
		 * 得到目标对象的缩放宽度
		 * @return
		 */	
		[Bindable("controlTransformTool")]
		public function get targetW() : Number
		{
			return _target.transform.pixelBounds.width;
		} 

		public function set targetW(w : Number) : void
		{
			oldToolMatrix = currentTargetMatrix;
			scaleXInteraction( w );
			apply( );
			dispatchEvent( new Event( TransformTool.CONTROL_TRANSFORM_TOOL ) );
			matrixChange( );
		} 

		/**
		 * 得到目标对象的缩放高度
		 * @return 
		 */
		[Bindable("controlTransformTool")]
		public function get targetH() : Number
		{
			return _target.transform.pixelBounds.height;
		} 

		public function set targetH(h : Number) : void
		{
			oldToolMatrix = currentTargetMatrix;
			scaleYInteraction( h );
			apply( );
			dispatchEvent( new Event( TransformTool.CONTROL_TRANSFORM_TOOL ) );
			matrixChange( );
		} 

		/**
		 * 得到目标对象原来的宽度
		 * @return 
		 */		
		public function get targetOldW() : Number
		{
			return _originalWidth;
		}

		/**
		 * 得到目标对象原来的高度
		 * @return 
		 */		
		public function get targetOldH() : Number
		{
			return _originalHeight;
		}

		/**
		 * 得到目标对象的X轴位置
		 * @return 
		 */	
		[Bindable("controlTransformTool")]
		public function get targetX() : Number
		{
			return _target.transform.pixelBounds.x;
		} 

		/**
		 * 设置目标对象的Y轴位置
		 * @param v
		 */
		public function set targetX(x : Number) : void
		{
			oldToolMatrix = currentTargetMatrix;
			moveInteraction( x, Infinity );
			apply( );
			dispatchEvent( new Event( TransformTool.CONTROL_TRANSFORM_TOOL ) );
			matrixChange( );
		} 

		/**
		 * 得到目标对象的Y轴位置
		 * @return 
		 */	
		[Bindable("controlTransformTool")]
		public function get targetY() : Number
		{
			return _target.transform.pixelBounds.y;
		} 

		/**
		 * 设置目标对象的Y轴位置
		 * @param v
		 * 
		 */			
		public function set targetY(y : Number) : void
		{
			oldToolMatrix = currentTargetMatrix;
			moveInteraction( Infinity, y );
			apply( );
			dispatchEvent( new Event( TransformTool.CONTROL_TRANSFORM_TOOL ) );
			matrixChange( );
		} 

		public function updateMatrixAndView(v : Matrix) : void
		{
			toolMatrix = v;
			dispatchEvent( new Event( TransformTool.CONTROL_TRANSFORM_TOOL ) );
		}

		private function matrixChange() : void
		{
			dispatchEvent( new TransformToolEvent( TransformToolEvent.TARGET_MATRIX_CHANGE, _target.uid,oldToolMatrix, currentTargetMatrix ) );
		}
		[Bindable("controlTransformTool")]
		public function get targetRotation() : Number
		{
			return 0;
		}

		public function setControlDisable() : void
		{
			_customCursorsEnabled = false;
			_cursorsEnabled = false;
			_customControlsEnabled = false;
			_registrationEnabled = false;
			_scaleEnabled = false;
			_skewEnabled = false;
			_moveEnabled = false;
			_rotationEnabled = false;
			_outlineEnabled = true;
			updateControlsEnabled( );
		}

		/**
		 * The display object the transform tool affects
		 */
		public function get target() : IMediaObject 
		{
			return _target;
		}

		public function set target(v : IMediaObject) : void 
		{
			var d : DisplayObject = v as DisplayObject;
			// null target, set target as null
			if (!d) 
			{
				if (_target) 
				{
					_target = null;
					_originalWidth = 0;					_originalHeight = 0;
					updateControlsVisible( );
					dispatchEvent( new Event( NEW_TARGET ) );
				}
				return;
			}else
			{
				// invalid target, do nothing
				if (d == _target || d == this || contains( d ) || (d is DisplayObjectContainer && (d as DisplayObjectContainer).contains( this ))) 
				{
					return;
				}
				
				// valid target, set and update
				_target = v;
				_originalWidth = _target.width;
				_originalHeight = _target.height;
				//set scale9Grid
				updateMatrix( );
				setNewRegistation( );
				updateControlsVisible( );
				if(Object( _target ).isLock)
				{
					setControlDisable( );
				}
				// raise to top of display list if applies
				if (_raiseNewTargets) 
				{
					raiseTarget( );
				}
			}
			// if not moving new targets, apply transforms
			if (!_moveNewTargets) 
			{
				apply( );
			}
			// send event; updates control points
			dispatchEvent( new Event( NEW_TARGET ) );
				
			
				
			// initiate move interaction if applies after controls updated
			if (_moveNewTargets && _moveEnabled && _moveControl) 
			{
				_currentControl = _moveControl;
				_currentControl.dispatchEvent( new MouseEvent( MouseEvent.MOUSE_DOWN ) );
			}
		}

		/**
		 * When true, new targets are placed at the top of their display list
		 * @see target
		 */
		public function get raiseNewTargets() : Boolean 
		{
			return _raiseNewTargets;
		}

		public function set raiseNewTargets(b : Boolean) : void 
		{
			_raiseNewTargets = b;
		}

		/**
		 * When true, new targets are immediately given a move interaction and can be dragged
		 * @see target
		 * @see moveEnabled
		 */
		public function get moveNewTargets() : Boolean 
		{
			return _moveNewTargets;
		}

		public function set moveNewTargets(b : Boolean) : void 
		{
			_moveNewTargets = b;
		}

		/**
		 * When true, the target instance scales with the tool as it is transformed.
		 * When false, transforms in the tool are only reflected when transforms are completed.
		 */
		public function get livePreview() : Boolean 
		{
			return _livePreview;
		}

		public function set livePreview(b : Boolean) : void 
		{
			_livePreview = b;
		}

		/**
		 * Controls the default Control sizes of controls used by the tool
		 */
		public function get controlSize() : Number 
		{
			return _controlSize;
		}

		public function set controlSize(n : Number) : void 
		{
			if (_controlSize != n) 
			{
				_controlSize = n;
				dispatchEvent( new Event( CONTROL_PREFERENCE ) );
			}
		}

		/**
		 * When true, counters transformations applied to controls by their parent containers
		 */
		public function get maintainControlForm() : Boolean 
		{
			return _maintainControlForm;
		}

		public function set maintainControlForm(b : Boolean) : void 
		{
			if (_maintainControlForm != b) 
			{
				_maintainControlForm = b;
				dispatchEvent( new Event( CONTROL_PREFERENCE ) );
			}
		}

		/**
		 * When true (default), the transform tool uses an invisible control using the shape of the current
		 * target to allow movement. This means any objects above the target but below the
		 * tool cannot be clicked on since this hidden control will be clicked on first
		 * (allowing you to move objects below others without selecting the objects on top).
		 * When false, the target itself is used for movement and any objects above the target
		 * become clickable preventing tool movement if the target itself is not clicked directly.
		 */
		public function get moveUnderObjects() : Boolean 
		{
			return _moveUnderObjects;
		}

		public function set moveUnderObjects(b : Boolean) : void 
		{
			if (_moveUnderObjects != b) 
			{
				_moveUnderObjects = b;
				dispatchEvent( new Event( CONTROL_PREFERENCE ) );
			}
		}

		public function get currentTargetMatrix() : Matrix
		{
			if(_target != null)	return _target.transform.matrix.clone( );
			return null;
		}

		/**
		 * The transform matrix of the tool
		 * as it exists in its on coordinate space
		 * @see globalMatrix
		 */
		public function get toolMatrix() : Matrix 
		{
			return _toolMatrix.clone( );
		}

		public function set toolMatrix(m : Matrix) : void 
		{
			updateMatrix( m, false );
			updateRegistration( );
			dispatchEvent( new Event( TRANSFORM_TOOL ) );
		}

		/**
		 * The transform matrix of the tool
		 * as it appears in global space
		 * @see toolMatrix
		 */
		public function get globalMatrix() : Matrix 
		{
			var _globalMatrix : Matrix = _toolMatrix.clone( );
			_globalMatrix.concat( transform.concatenatedMatrix );
			return _globalMatrix;
		}

		public function set globalMatrix(m : Matrix) : void 
		{
			updateMatrix( m );
			updateRegistration( );
			dispatchEvent( new Event( TRANSFORM_TOOL ) );
		}

		/**
		 * The location of the registration point in the tool. Note: registration
		 * points are tool-specific.  If you change the registration point of a
		 * target, the new registration will only be reflected in the tool used
		 * to change that point.
		 * @see registrationEnabled
		 * @see rememberRegistration
		 */
		public function get registration() : Point 
		{
			return _registration.clone( );
		}

		public function set registration(p : Point) : void 
		{
			_registration = p.clone( );
			innerRegistration = toolInvertedMatrix.transformPoint( _registration );
			
			if (_rememberRegistration) 
			{
				// log new registration point for the next
				// time this target is selected
				registrationLog[_target] = innerRegistration;
			}
			dispatchEvent( new Event( TRANSFORM_TOOL ) );
		}

		/**
		 * The current control being used in the tool if being manipulated.
		 * This value is null if the user is not transforming the tool.
		 */
		public function get currentControl() : TransformToolControl 
		{
			return _currentControl;
		}

		/**
		 * Allows or disallows users to move the tool
		 */
		public function get moveEnabled() : Boolean 
		{
			return _moveEnabled;
		}

		public function set moveEnabled(b : Boolean) : void 
		{
			if (_moveEnabled != b) 
			{
				_moveEnabled = b;
				updateControlsEnabled( );
			}
		}

		public function set scale9GridEnabled(b : Boolean) : void 
		{			
			_scale9GridEnabled = b;
		}

		public function get scale9GridEnabled() : Boolean 
		{
			return _scale9GridEnabled;
		}

		/**
		 * Allows or disallows users to see and move the registration point
		 * @see registration
		 * @see rememberRegistration
		 */
		public function get registrationEnabled() : Boolean 
		{
			return _registrationEnabled;
		}

		public function set registrationEnabled(b : Boolean) : void 
		{
			if (_registrationEnabled != b) 
			{
				_registrationEnabled = b;
				updateControlsEnabled( );
			}
		}

		/**
		 * Allows or disallows users to see and adjust rotation controls
		 */
		public function get rotationEnabled() : Boolean 
		{
			return _rotationEnabled;
		}

		public function set rotationEnabled(b : Boolean) : void 
		{
			if (_rotationEnabled != b) 
			{
				_rotationEnabled = b;
				updateControlsEnabled( );
			}
		}

		/**
		 * Allows or disallows users to see and adjust scale controls
		 */
		public function get scaleEnabled() : Boolean 
		{
			return _scaleEnabled;
		}

		public function set scaleEnabled(b : Boolean) : void 
		{
			if (_scaleEnabled != b) 
			{
				_scaleEnabled = b;
				updateControlsEnabled( );
			}
		}

		/**
		 * Allows or disallows users to see and adjust skew controls
		 */
		public function get skewEnabled() : Boolean 
		{
			return _skewEnabled;
		}

		public function set skewEnabled(b : Boolean) : void 
		{
			if (_skewEnabled != b) 
			{
				_skewEnabled = b;
				updateControlsEnabled( );
			}
		}

		/**
		 * Allows or disallows users to see tool boundry outlines
		 */
		public function get outlineEnabled() : Boolean 
		{
			return _outlineEnabled;
		}

		public function set outlineEnabled(b : Boolean) : void 
		{
			if (_outlineEnabled != b) 
			{
				_outlineEnabled = b;
				updateControlsEnabled( );
			}
		}

		/**
		 * Allows or disallows users to see native cursors
		 * @see addCursor
		 * @see removeCursor
		 * @see customCursorsEnabled
		 */
		public function get cursorsEnabled() : Boolean 
		{
			return _cursorsEnabled;
		}

		public function set cursorsEnabled(b : Boolean) : void 
		{
			if (_cursorsEnabled != b) 
			{
				_cursorsEnabled = b;
				updateControlsEnabled( );
			}
		}

		/**
		 * Allows or disallows users to see and use custom controls
		 * @see addControl
		 * @see removeControl
		 * @see customCursorsEnabled
		 */
		public function get customControlsEnabled() : Boolean 
		{
			return _customControlsEnabled;
		}

		public function set customControlsEnabled(b : Boolean) : void 
		{
			if (_customControlsEnabled != b) 
			{
				_customControlsEnabled = b;
				updateControlsEnabled( );
				dispatchEvent( new Event( CONTROL_PREFERENCE ) );
			}
		}

		/**
		 * Allows or disallows users to see custom cursors
		 * @see addCursor
		 * @see removeCursor
		 * @see cursorsEnabled
		 * @see customControlsEnabled
		 */
		public function get customCursorsEnabled() : Boolean 
		{
			return _customCursorsEnabled;
		}

		public function set customCursorsEnabled(b : Boolean) : void 
		{
			if (_customCursorsEnabled != b) 
			{
				_customCursorsEnabled = b;
				updateControlsEnabled( );
				dispatchEvent( new Event( CONTROL_PREFERENCE ) );
			}
		}

		/**
		 * Allows or disallows users to see custom cursors
		 * @see registration
		 */
		public function get rememberRegistration() : Boolean 
		{
			return _rememberRegistration;
		}

		public function set rememberRegistration(b : Boolean) : void 
		{
			_rememberRegistration = b;
			if (!_rememberRegistration) 
			{
				registrationLog = new Dictionary( true );
			}
		}

		/**
		 * Allows constraining of scale transformations that scale along both X and Y.
		 * @see constrainRotation
		 */
		public function get constrainScale() : Boolean 
		{
			return _constrainScale;
		}

		public function set constrainScale(b : Boolean) : void 
		{
			if (_constrainScale != b) 
			{
				_constrainScale = b;
				dispatchEvent( new Event( CONTROL_PREFERENCE ) );
			}
		}

		/**
		 * Allows constraining of rotation transformations by an angle
		 * @see constrainRotationAngle
		 * @see constrainScale
		 */
		public function get constrainRotation() : Boolean 
		{
			return _constrainRotation;
		}

		public function set constrainRotation(b : Boolean) : void 
		{
			if (_constrainRotation != b) 
			{
				_constrainRotation = b;
				dispatchEvent( new Event( CONTROL_PREFERENCE ) );
			}
		}

		/**
		 * The angle at which rotation is constrainged when constrainRotation is true
		 * @see constrainRotation
		 */
		public function get constrainRotationAngle() : Number 
		{
			return _constrainRotationAngle * 180 / Math.PI;
		}

		public function set constrainRotationAngle(n : Number) : void 
		{
			var angleInRadians : Number = n * Math.PI / 180;
			if (_constrainRotationAngle != angleInRadians) 
			{
				_constrainRotationAngle = angleInRadians;
				dispatchEvent( new Event( CONTROL_PREFERENCE ) );
			}
		}

		/**
		 * The maximum scaleX allowed to be applied to a target
		 */
		public function get maxScaleX() : Number 
		{
			return _maxScaleX;
		}

		public function set maxScaleX(n : Number) : void 
		{
			_maxScaleX = n;
		}

		/**
		 * The maximum scaleY allowed to be applied to a target
		 */
		public function get maxScaleY() : Number 
		{
			return _maxScaleY;
		}

		public function set maxScaleY(n : Number) : void 
		{
			_maxScaleY = n;
		}

		public function get boundsTopLeft() : Point 
		{ 
			return _boundsTopLeft.clone( ); 
		}

		public function get boundsTop() : Point 
		{ 
			return _boundsTop.clone( ); 
		}

		public function get boundsTopRight() : Point 
		{ 
			return _boundsTopRight.clone( ); 
		}

		public function get boundsRight() : Point 
		{ 
			return _boundsRight.clone( ); 
		}

		public function get boundsBottomRight() : Point 
		{ 
			return _boundsBottomRight.clone( ); 
		}

		public function get boundsBottom() : Point 
		{ 
			return _boundsBottom.clone( ); 
		}

		public function get boundsBottomLeft() : Point 
		{ 
			return _boundsBottomLeft.clone( ); 
		}

		public function get boundsLeft() : Point 
		{ 
			return _boundsLeft.clone( ); 
		}

		public function get boundsCenter() : Point 
		{ 
			return _boundsCenter.clone( ); 
		}

		public function get mouse() : Point 
		{ 
			return new Point( mouseX, mouseY ); 
		}

		public function get moveControl() : TransformToolControl 
		{ 
			return _moveControl; 
		}

		public function get registrationControl() : TransformToolControl 
		{ 
			return _registrationControl; 
		}

		public function get outlineControl() : TransformToolControl 
		{ 
			return _outlineControl; 
		}

		public function get scaleTopLeftControl() : TransformToolControl 
		{ 
			return _scaleTopLeftControl; 
		}

		public function get scaleTopControl() : TransformToolControl 
		{ 
			return _scaleTopControl; 
		}

		public function get scaleTopRightControl() : TransformToolControl 
		{ 
			return _scaleTopRightControl; 
		}

		public function get scaleRightControl() : TransformToolControl 
		{ 
			return _scaleRightControl; 
		}

		public function get scaleBottomRightControl() : TransformToolControl 
		{ 
			return _scaleBottomRightControl; 
		}

		public function get scaleBottomControl() : TransformToolControl 
		{ 
			return _scaleBottomControl; 
		}

		public function get scaleBottomLeftControl() : TransformToolControl 
		{ 
			return _scaleBottomLeftControl; 
		}

		public function get scaleLeftControl() : TransformToolControl 
		{ 
			return _scaleLeftControl; 
		}

		public function get rotationTopLeftControl() : TransformToolControl 
		{ 
			return _rotationTopLeftControl; 
		}

		public function get rotationTopRightControl() : TransformToolControl 
		{ 
			return _rotationTopRightControl; 
		}

		public function get rotationBottomRightControl() : TransformToolControl 
		{ 
			return _rotationBottomRightControl; 
		}

		public function get rotationBottomLeftControl() : TransformToolControl 
		{ 
			return _rotationBottomLeftControl; 
		}

		public function get skewTopControl() : TransformToolControl 
		{ 
			return _skewTopControl; 
		}

		public function get skewRightControl() : TransformToolControl 
		{ 
			return _skewRightControl; 
		}

		public function get skewBottomControl() : TransformToolControl 
		{ 
			return _skewBottomControl; 
		}

		public function get skewLeftControl() : TransformToolControl 
		{ 
			return _skewLeftControl; 
		}

		public function get moveCursor() : TransformToolCursor 
		{ 
			return _moveCursor; 
		}

		public function get registrationCursor() : TransformToolCursor 
		{ 
			return _registrationCursor; 
		}

		public function get rotationCursor() : TransformToolCursor 
		{ 
			return _rotationCursor; 
		}

		public function get scaleCursor() : TransformToolCursor 
		{ 
			return _scaleCursor; 
		}

		public function get skewCursor() : TransformToolCursor 
		{ 
			return _skewCursor; 
		}

		
		/**
		 * TransformTool Constructor.
		 * Creates new instances of the transform tool
		 */
		public function TransformTool() 
		{
			createControls( );
		}

		/**
		 * Provides a string representation of the transform instance
		 */
		override public function toString() : String 
		{
			return "[Transform Tool: target=" + String( _target ) + "]" ;
		}

		// Setup
		private function createControls() : void 
		{
			
			// defining controls
			_moveControl = new TransformToolMoveShape( "move", moveInteraction );
			_registrationControl = new TransformToolRegistrationControl( REGISTRATION, registrationInteraction, "registration" );
			_rotationTopLeftControl = new TransformToolRotateControl( ROTATION_TOP_LEFT, rotationInteraction, "boundsTopLeft" );
			_rotationTopRightControl = new TransformToolRotateControl( ROTATION_TOP_RIGHT, rotationInteraction, "boundsTopRight" );
			_rotationBottomRightControl = new TransformToolRotateControl( ROTATION_BOTTOM_RIGHT, rotationInteraction, "boundsBottomRight" );
			_rotationBottomLeftControl = new TransformToolRotateControl( ROTATION_BOTTOM_LEFT, rotationInteraction, "boundsBottomLeft" );
			_scaleTopLeftControl = new TransformToolScaleControl( SCALE_TOP_LEFT, scaleBothInteraction, "boundsTopLeft" );
			_scaleTopControl = new TransformToolScaleControl( SCALE_TOP, scaleYInteraction, "boundsTop" );
			_scaleTopRightControl = new TransformToolScaleControl( SCALE_TOP_RIGHT, scaleBothInteraction, "boundsTopRight" );
			_scaleRightControl = new TransformToolScaleControl( SCALE_RIGHT, scaleXInteraction, "boundsRight" );
			_scaleBottomRightControl = new TransformToolScaleControl( SCALE_BOTTOM_RIGHT, scaleBothInteraction, "boundsBottomRight" );
			_scaleBottomControl = new TransformToolScaleControl( SCALE_BOTTOM, scaleYInteraction, "boundsBottom" );
			_scaleBottomLeftControl = new TransformToolScaleControl( SCALE_BOTTOM_LEFT, scaleBothInteraction, "boundsBottomLeft" );
			_scaleLeftControl = new TransformToolScaleControl( SCALE_LEFT, scaleXInteraction, "boundsLeft" );
			_skewTopControl = new TransformToolSkewBar( SKEW_TOP, skewXInteraction, "boundsTopRight", "boundsTopLeft", "boundsTopRight" );
			_skewRightControl = new TransformToolSkewBar( SKEW_RIGHT, skewYInteraction, "boundsBottomRight", "boundsTopRight", "boundsBottomRight" );
			_skewBottomControl = new TransformToolSkewBar( SKEW_BOTTOM, skewXInteraction, "boundsBottomLeft", "boundsBottomRight", "boundsBottomLeft" );
			_skewLeftControl = new TransformToolSkewBar( SKEW_LEFT, skewYInteraction, "boundsTopLeft", "boundsBottomLeft", "boundsTopLeft" );
			
			// defining cursors
			_moveCursor = new TransformToolMoveCursor( );
			_moveCursor.addReference( _moveControl );
			
			_registrationCursor = new TransformToolRegistrationCursor( );
			_registrationCursor.addReference( _registrationControl );
			
			_rotationCursor = new TransformToolRotateCursor( );
			_rotationCursor.addReference( _rotationTopLeftControl );
			_rotationCursor.addReference( _rotationTopRightControl );
			_rotationCursor.addReference( _rotationBottomRightControl );
			_rotationCursor.addReference( _rotationBottomLeftControl );
			
			_scaleCursor = new TransformToolScaleCursor( );
			_scaleCursor.addReference( _scaleTopLeftControl );
			_scaleCursor.addReference( _scaleTopControl );
			_scaleCursor.addReference( _scaleTopRightControl );
			_scaleCursor.addReference( _scaleRightControl );
			_scaleCursor.addReference( _scaleBottomRightControl );
			_scaleCursor.addReference( _scaleBottomControl );
			_scaleCursor.addReference( _scaleBottomLeftControl );
			_scaleCursor.addReference( _scaleLeftControl );
			
			_skewCursor = new TransformToolSkewCursor( );
			_skewCursor.addReference( _skewTopControl );
			_skewCursor.addReference( _skewRightControl );
			_skewCursor.addReference( _skewBottomControl );
			_skewCursor.addReference( _skewLeftControl );
			
			// adding controls
			addToolControl( moveControls, _moveControl );
			addToolControl( registrationControls, _registrationControl );
			addToolControl( rotateControls, _rotationTopLeftControl );
			addToolControl( rotateControls, _rotationTopRightControl );
			addToolControl( rotateControls, _rotationBottomRightControl );
			addToolControl( rotateControls, _rotationBottomLeftControl );
			addToolControl( scaleControls, _scaleTopControl );
			addToolControl( scaleControls, _scaleRightControl );
			addToolControl( scaleControls, _scaleBottomControl );
			addToolControl( scaleControls, _scaleLeftControl );
			addToolControl( scaleControls, _scaleTopLeftControl );
			addToolControl( scaleControls, _scaleTopRightControl );
			addToolControl( scaleControls, _scaleBottomRightControl );
			addToolControl( scaleControls, _scaleBottomLeftControl );
			addToolControl( skewControls, _skewTopControl );
			addToolControl( skewControls, _skewRightControl );
			addToolControl( skewControls, _skewBottomControl );
			addToolControl( skewControls, _skewLeftControl );
			addToolControl( lines, new TransformToolOutline( "outline" ), false );
			
			// adding cursors
			addToolControl( cursors, _moveCursor, false );
			addToolControl( cursors, _registrationCursor, false );
			addToolControl( cursors, _rotationCursor, false );
			addToolControl( cursors, _scaleCursor, false );
			addToolControl( cursors, _skewCursor, false );
			
			
			updateControlsEnabled( );
		}

		private function addToolControl(container : Sprite, control : TransformToolControl, interactive : Boolean = true) : void 
		{
			control.transformTool = this;
			if (interactive) 
			{
				control.addEventListener( MouseEvent.MOUSE_DOWN, startInteractionHandler );	
			}
			container.addChild( control );
			control.dispatchEvent( new Event( CONTROL_INIT ) );
		}

		/**
		 * Allows you to add a custom control to the tool
		 * @see removeControl
		 * @see addCursor
		 * @see removeCursor
		 */
		public function addControl(control : TransformToolControl) : void 
		{
			addToolControl( customControls, control );
		}

		/**
		 * Allows you to remove a custom control to the tool
		 * @see addControl
		 * @see addCursor
		 * @see removeCursor
		 */
		public function removeControl(control : TransformToolControl) : TransformToolControl 
		{
			if (customControls.contains( control )) 
			{
				customControls.removeChild( control );
				return control;
			}
			return null;
		}

		/**
		 * Allows you to add a custom cursor to the tool
		 * @see removeCursor
		 * @see addControl
		 * @see removeControl
		 */
		public function addCursor(cursor : TransformToolCursor) : void 
		{
			addToolControl( customCursors, cursor );
		}

		/**
		 * Allows you to remove a custom cursor to the tool
		 * @see addCursor
		 * @see addControl
		 * @see removeControl
		 */
		public function removeCursor(cursor : TransformToolCursor) : TransformToolCursor 
		{
			if (customCursors.contains( cursor )) 
			{
				customCursors.removeChild( cursor );
				return cursor;
			}
			return null;
		}

		/**
		 * Allows you to change the appearance of default controls
		 * @see addControl
		 * @see removeControl
		 */
		public function setSkin(controlName : String, skin : DisplayObject) : void 
		{
			var control : TransformToolInternalControl = getControlByName( controlName );
			if (control) 
			{
				control.skin = skin;
			}
		}

		/**
		 * Allows you to get the skin of an existing control.
		 * If one was not set, null is returned
		 * @see addControl
		 * @see removeControl
		 */
		public function getSkin(controlName : String) : DisplayObject 
		{
			var control : TransformToolInternalControl = getControlByName( controlName );
			return control.skin;
		}

		private function getControlByName(controlName : String) : TransformToolInternalControl 
		{
			var control : TransformToolInternalControl;
			var containers : Array = new Array( skewControls, registrationControls, cursors, rotateControls, scaleControls );
			var i : int = containers.length;
			while (i-- && control == null) 
			{
				control = containers[i].getChildByName( controlName ) as TransformToolInternalControl;
			}
			return control;
		}

		// Interaction Handlers
		public function startInteractionHandler(event : MouseEvent) : void 
		{
			_currentControl = event.currentTarget as TransformToolControl;
			if (_currentControl) 
			{
				setupInteraction( );
			}
		}

		private function setupInteraction() : void 
		{
			updateMatrix( );
			apply( );
			dispatchEvent( new Event( CONTROL_DOWN ) );
			
			// mouse offset to allow interaction from desired point
			mouseOffset = (_currentControl && _currentControl.referencePoint) ? _currentControl.referencePoint.subtract( new Point( mouseX, mouseY ) ) : new Point( 0, 0 );
			updateMouse( );
			
			// set variables for interaction reference
			interactionStart = mouseLoc.clone( );
			innerInteractionStart = innerMouseLoc.clone( );
			interactionStartMatrix = _toolMatrix.clone( );
			interactionStartAngle = distortAngle( );
			
			if (stage) 
			{
				// setup stage events to manage control interaction
				stage.addEventListener( MouseEvent.MOUSE_MOVE, interactionHandler );
				stage.addEventListener( MouseEvent.MOUSE_UP, endInteractionHandler, false );
				stage.addEventListener( MouseEvent.MOUSE_UP, endInteractionHandler, true );
			}
		}

		private function interactionHandler(event : MouseEvent) : void 
		{
			//trace( "interactionHandler" );
			//第一次出发变更，记录最初就矩阵值
			if(!isInteractioned)	oldToolMatrix = currentTargetMatrix;
			isInteractioned = true;
			// define mouse position for interaction
			updateMouse( );
			
			// use original toolMatrix for reference of interaction
			_toolMatrix = interactionStartMatrix.clone( );
			
			// dispatch events that let controls do their thing
			dispatchEvent( new Event( CONTROL_MOVE ) );
			dispatchEvent( new Event( CONTROL_TRANSFORM_TOOL ) );
			
			if (_livePreview) 
			{
				// update target if applicable
				apply( );
			}
			// smooth sailing
			event.updateAfterEvent( );
		}

		private function endInteractionHandler(event : MouseEvent) : void 
		{
			//trace( "endInteractionHandler" );
			if(isInteractioned)	matrixChange( );
			isInteractioned = false;
			if (event.eventPhase == EventPhase.BUBBLING_PHASE || !(event.currentTarget is Stage)) 
			{
				// ignore unrelated events received by stage
				return;
			}
			
			if (!_livePreview) 
			{
				// update target if applicable
				apply( );
			}
			
			// get stage reference from event in case
			// stage is no longer accessible from this instance
			var stageRef : Stage = event.currentTarget as Stage;
			stageRef.removeEventListener( MouseEvent.MOUSE_MOVE, interactionHandler );
			stageRef.removeEventListener( MouseEvent.MOUSE_UP, endInteractionHandler, false );
			stageRef.removeEventListener( MouseEvent.MOUSE_UP, endInteractionHandler, true );
			dispatchEvent( new Event( CONTROL_UP ) );
			_currentControl = null;
		}

		// Interaction Transformations
		/**
		 * Control Interaction.  Moves the tool
		 */
		public function moveInteraction(x : Number = Infinity,y : Number = Infinity) : void 
		{
			if(outOfBorderline != null)
			{
				if(outOfBorderline( ))
				{
					return;
				}
			}
			if(x == Infinity && y == Infinity)
			{
				var moveLoc : Point = mouseLoc.subtract( interactionStart );
				_toolMatrix.tx += moveLoc.x;
				_toolMatrix.ty += moveLoc.y;
			}
			else if(x != Infinity && y != Infinity)
			{
				_toolMatrix.tx = x;
				_toolMatrix.ty = y;
			}
			else if(x != Infinity)
				_toolMatrix.tx = x;
			else if(y != Infinity)
				_toolMatrix.ty = y;
			else
			    trace( "error" );

			updateRegistration( );
			completeInteraction( );
		}

		/**
		 * Control Interaction.  Moves the registration point
		 */
		public function registrationInteraction(x : Number = Infinity,y : Number = Infinity) : void 
		{
			if(x != Infinity && y != Infinity)
			{
				_registration.x = x;
				_registration.y = y;
			}
			else
			{
				// move registration point
				_registration.x = mouseLoc.x;
				_registration.y = mouseLoc.y;
			}
			//trace(_registration.x,_registration.y);
			innerRegistration = toolInvertedMatrix.transformPoint( _registration );
			//trace("innerRegistration="+innerRegistration.x,innerRegistration.y);
			if (_rememberRegistration) 
			{
				// log new registration point for the next
				// time this target is selected
				registrationLog[_target] = innerRegistration;
			}
			completeInteraction( );
		}

		/**
		 * Control Interaction.  Rotates the tool
		 */
		public function rotationInteraction(degree : Number = Infinity) : void 
		{
			// rotate in global transform
			var globalMatrix : Matrix = transform.concatenatedMatrix;
			var globalInvertedMatrix : Matrix = globalMatrix.clone( );
			globalInvertedMatrix.invert( );
			_toolMatrix.concat( globalMatrix );
			
			// get change in rotation
			var angle : Number = distortAngle( ) - interactionStartAngle;
			
			if (_constrainRotation) 
			{
				// constrain rotation based on constrainRotationAngle
				if (angle > Math.PI) 
				{
					angle -= Math.PI * 2;
				}else if (angle < -Math.PI) 
				{
					angle += Math.PI * 2;
				}
				angle = Math.round( angle / _constrainRotationAngle ) * _constrainRotationAngle;
			}
			// apply rotation to toolMatrix
			_toolMatrix.rotate( angle );
			
			_toolMatrix.concat( globalInvertedMatrix );
			completeInteraction( true );
		}

		/**
		 * Control Interaction.  Scales the tool along the X axis
		 */
		public function scaleXInteraction(w : Number = Infinity) : void 
		{
			if(w != Infinity)
			{
				_toolMatrix.scale( w / targetW, 1 );
			}
            else
			{
				// get distortion offset vertical movement
				var distortH : Point = distortOffset( new Point( innerMouseLoc.x, innerInteractionStart.y ), innerInteractionStart.x - innerRegistration.x );
				// update the matrix for vertical scale
				_toolMatrix.a += distortH.x;
				_toolMatrix.b += distortH.y;
				//trace( _target.rotation );
				//trace( _toolMatrix.a, _toolMatrix.b );
				//trace(distortH.x,distortH.y,_toolMatrix.a,_toolMatrix.b)
			}
			completeInteraction( true );
		}

		/**
		 * Control Interaction.  Scales the tool along the Y axis
		 */
		public function scaleYInteraction(h : Number = Infinity) : void 
		{
			if(h != Infinity)
			{
				_toolMatrix.scale( 1, h / targetH );
			}
			else
			{
				// get distortion offset vertical movement
				var distortV : Point = distortOffset( new Point( innerInteractionStart.x, innerMouseLoc.y ), innerInteractionStart.y - innerRegistration.y );
				// update the matrix for vertical scale
				_toolMatrix.c += distortV.x;
				_toolMatrix.d += distortV.y;
			}
			completeInteraction( true );
		}

		/**
		 * Control Interaction.  Scales the tool along both the X and Y axes
		 */
		public function scaleBothInteraction(x : Number = Infinity,y : Number = Infinity) : void 
		{
			// mouse reference, may change from innerMouseLoc if constraining
			var innerMouseRef : Point = innerMouseLoc.clone( );
			
			if (_constrainScale) 
			{
				
				// how much the mouse has moved from starting the interaction
				var moved : Point = innerMouseLoc.subtract( innerInteractionStart );
				
				// the relationship of the start location to the registration point
				var regOffset : Point = innerInteractionStart.subtract( innerRegistration );
				
				// find the ratios between movement and the registration offset
				var ratioH : Number = regOffset.x ? moved.x / regOffset.x : 0;
				var ratioV : Number = regOffset.y ? moved.y / regOffset.y : 0;
				
				// have the larger of the movement distances brought down
				// based on the lowest ratio to fit the registration offset
				if (ratioH > ratioV) 
				{
					innerMouseRef.x = innerInteractionStart.x + regOffset.x * ratioV;
				}else
				{
					innerMouseRef.y = innerInteractionStart.y + regOffset.y * ratioH;
				}
			}
			
			// get distortion offsets for both vertical and horizontal movements
			var distortH : Point = distortOffset( new Point( innerMouseRef.x, innerInteractionStart.y ), innerInteractionStart.x - innerRegistration.x );
			var distortV : Point = distortOffset( new Point( innerInteractionStart.x, innerMouseRef.y ), innerInteractionStart.y - innerRegistration.y );
			
			// update the matrix for both scales
			_toolMatrix.a += distortH.x;
			_toolMatrix.b += distortH.y;
			_toolMatrix.c += distortV.x;
			_toolMatrix.d += distortV.y;
			completeInteraction( true );
		}

		/**
		 * Control Interaction.  Skews the tool along the X axis
		 */
		public function skewXInteraction(x : Number = Infinity) : void 
		{
			var distortH : Point = distortOffset( new Point( innerMouseLoc.x, innerInteractionStart.y ), innerInteractionStart.y - innerRegistration.y );
			_toolMatrix.c += distortH.x;
			_toolMatrix.d += distortH.y;
			completeInteraction( true );
		}

		/**
		 * Control Interaction.  Skews the tool along the Y axis
		 */
		public function skewYInteraction(y : Number = Infinity) : void 
		{
			var distortV : Point = distortOffset( new Point( innerInteractionStart.x, innerMouseLoc.y ), innerInteractionStart.x - innerRegistration.x );
			_toolMatrix.a += distortV.x;
			_toolMatrix.b += distortV.y;
			completeInteraction( true );
		}

		private function distortOffset(offset : Point, regDiff : Number) : Point 
		{
			// get changes in matrix combinations based on targetBounds
			var ratioH : Number = regDiff ? targetBounds.width / regDiff : 0;
			var ratioV : Number = regDiff ? targetBounds.height / regDiff : 0;
			offset = interactionStartMatrix.transformPoint( offset ).subtract( interactionStart );
			offset.x *= targetBounds.width ? ratioH / targetBounds.width : 0;
			offset.y *= targetBounds.height ? ratioV / targetBounds.height : 0;
			return offset;
		}

		private function completeInteraction(offsetReg : Boolean = false) : void 
		{
			enforceLimits( );
			if (offsetReg) 
			{
				// offset of registration to have transformations based around
				// custom registration point
				var offset : Point = _registration.subtract( _toolMatrix.transformPoint( innerRegistration ) );
				_toolMatrix.tx += offset.x;
				_toolMatrix.ty += offset.y;
			}
			updateBounds( );
		}

		// Information
		private function distortAngle() : Number 
		{
			// use global mouse and registration
			var globalMatrix : Matrix = transform.concatenatedMatrix;
			var gMouseLoc : Point = globalMatrix.transformPoint( mouseLoc );
			var gRegistration : Point = globalMatrix.transformPoint( _registration );
			
			// distance and angle of mouse from registration
			var offset : Point = gMouseLoc.subtract( gRegistration );
			return Math.atan2( offset.y, offset.x );
		}

		// Updates
		private function updateMouse() : void 
		{
			mouseLoc = new Point( mouseX, mouseY ).add( mouseOffset );
			innerMouseLoc = toolInvertedMatrix.transformPoint( mouseLoc );
		}

		private function updateMatrix(useMatrix : Matrix = null, counterTransform : Boolean = true) : void 
		{
			if (_target) 
			{
				_toolMatrix = useMatrix ? useMatrix.clone( ) : _target.transform.concatenatedMatrix.clone( );
				if (counterTransform) 
				{
					// counter transform of the parents of the tool
					var current : Matrix = transform.concatenatedMatrix;
					current.invert( );
					_toolMatrix.concat( current );
				}
				enforceLimits( );
				toolInvertedMatrix = _toolMatrix.clone( );
				toolInvertedMatrix.invert( );
				updateBounds( );
			}
		}

		private function updateBounds() : void 
		{
			if (_target) 
			{
				// update tool bounds based on target bounds
				targetBounds = _target.getBounds( DisplayObject(_target) );
				_boundsTopLeft = _toolMatrix.transformPoint( new Point( targetBounds.left, targetBounds.top ) );
				_boundsTopRight = _toolMatrix.transformPoint( new Point( targetBounds.right, targetBounds.top ) );
				_boundsBottomRight = _toolMatrix.transformPoint( new Point( targetBounds.right, targetBounds.bottom ) );
				_boundsBottomLeft = _toolMatrix.transformPoint( new Point( targetBounds.left, targetBounds.bottom ) );
				_boundsTop = Point.interpolate( _boundsTopLeft, _boundsTopRight, .5 );
				_boundsRight = Point.interpolate( _boundsTopRight, _boundsBottomRight, .5 );
				_boundsBottom = Point.interpolate( _boundsBottomRight, _boundsBottomLeft, .5 );
				_boundsLeft = Point.interpolate( _boundsBottomLeft, _boundsTopLeft, .5 );
				_boundsCenter = Point.interpolate( _boundsTopLeft, _boundsBottomRight, .5 );
			}
		}

		private function updateControlsVisible() : void 
		{
			// show toolSprites only if there is a valid target
			var isChild : Boolean = contains( toolSprites );
			if (_target) 
			{
				if (!isChild) 
				{
					addChild( toolSprites );
				}				
			}else if (isChild) 
			{
				removeChild( toolSprites );
			}
		}

		private function updateControlsEnabled() : void 
		{
			// highest arrangement
			updateControlContainer( customCursors, _customCursorsEnabled );
			updateControlContainer( cursors, _cursorsEnabled );
			updateControlContainer( customControls, _customControlsEnabled );
			updateControlContainer( registrationControls, _registrationEnabled );
			updateControlContainer( scaleControls, _scaleEnabled );
			updateControlContainer( skewControls, _skewEnabled );
			updateControlContainer( moveControls, _moveEnabled );
			updateControlContainer( rotateControls, _rotationEnabled );
			updateControlContainer( lines, _outlineEnabled );
			// lowest arrangement
		}

		private function updateControlContainer(container : Sprite, enabled : Boolean) : void 
		{
			var isChild : Boolean = toolSprites.contains( container );
			if (enabled) 
			{
				// add child or sent to bottom if enabled
				if (isChild) 
				{
					toolSprites.setChildIndex( container, 0 );
				}else
				{
					toolSprites.addChildAt( container, 0 );
				}
			}else if (isChild) 
			{
				// removed if disabled
				toolSprites.removeChild( container );
			}
		}

		private function updateRegistration() : void 
		{
			_registration = _toolMatrix.transformPoint( innerRegistration );
		}

		private function enforceLimits() : void 
		{
			
			var currScale : Number;
			var angle : Number;
			var enforced : Boolean = false;
			
			// use global matrix
			var _globalMatrix : Matrix = _toolMatrix.clone( );
			_globalMatrix.concat( transform.concatenatedMatrix );
			
			// check current scale in X
			currScale = Math.sqrt( _globalMatrix.a * _globalMatrix.a + _globalMatrix.b * _globalMatrix.b );
			if (currScale > _maxScaleX) 
			{
				// set scaleX to no greater than _maxScaleX
				angle = Math.atan2( _globalMatrix.b, _globalMatrix.a );
				_globalMatrix.a = Math.cos( angle ) * _maxScaleX;
				_globalMatrix.b = Math.sin( angle ) * _maxScaleX;
				enforced = true;
			}
			
			// check current scale in Y
			currScale = Math.sqrt( _globalMatrix.c * _globalMatrix.c + _globalMatrix.d * _globalMatrix.d );
			if (currScale > _maxScaleY) 
			{
				// set scaleY to no greater than _maxScaleY
				angle = Math.atan2( _globalMatrix.c, _globalMatrix.d );
				_globalMatrix.d = Math.cos( angle ) * _maxScaleY;
				_globalMatrix.c = Math.sin( angle ) * _maxScaleY;
				enforced = true;
			}
			
			
			// if scale was enforced, apply to _toolMatrix
			if (enforced) 
			{
				_toolMatrix = _globalMatrix;
				var current : Matrix = transform.concatenatedMatrix;
				current.invert( );
				_toolMatrix.concat( current );
			}
		}

		// Render
		private function setNewRegistation() : void 
		{
			if (_rememberRegistration && _target in registrationLog) 
			{
				
				// retrieved saved reg point in log
				//var savedReg:Point = registrationLog[_target];
				innerRegistration = registrationLog[_target];
			}else
			{
				
				// use internal own point
				innerRegistration = new Point( 0, 0 );
			}
			updateRegistration( );
		}

		private function raiseTarget() : void 
		{
			// set target to last object in display list
			var index : int = _target.parent.numChildren - 1;
			_target.parent.setChildIndex( DisplayObject(_target), index );
			
			// if this tool is in the same display list
			// raise it to the top above target
			if (_target.parent == parent) 
			{
				parent.setChildIndex( this, index );
			}
		}

		/**
		 * Draws the transform tool over its target instance
		 */
		public function draw() : void 
		{
			// update the matrix and draw controls
			updateMatrix( );
			dispatchEvent( new Event( TRANSFORM_TOOL ) );
		}

		/**
		 * Applies the current tool transformation to its target instance
		 */
		public function apply() : void 
		{
			if (_target) 
			{
				
				// get matrix to apply to target
				var applyMatrix : Matrix = _toolMatrix.clone( );
				applyMatrix.concat( transform.concatenatedMatrix );
				
				// if target has a parent, counter parent transformations
				if (_target.parent) 
				{
					var invertMatrix : Matrix = target.parent.transform.concatenatedMatrix;
					invertMatrix.invert( );
					applyMatrix.concat( invertMatrix );
				}
				
				// set target's matrix
				_target.transform.matrix = applyMatrix;
				
				dispatchEvent( new Event( TRANSFORM_TARGET ) );
			}
		}
	}
}

import flash.display.DisplayObject;
import flash.display.InteractiveObject;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;

import org.asblog.transform.TransformTool;
import org.asblog.transform.TransformToolControl;
import org.asblog.transform.TransformToolCursor;

// Controls

class TransformToolInternalControl extends TransformToolControl 
{

	public var interactionMethod : Function;
	public var referenceName : String;
	public var _skin : DisplayObject;

	public function set skin(skin : DisplayObject) : void 
	{
		if (_skin && contains( _skin )) 
		{
			removeChild( _skin );
		}
		_skin = skin;
		if (_skin) 
		{
			addChild( _skin );
		}
		draw( );
	}

	public function get skin() : DisplayObject 
	{
		return _skin;
	}

	override public function get referencePoint() : Point 
	{
		if (referenceName in _transformTool) 
		{
			return _transformTool[referenceName];
		}
		return null;
	}

	/*
	 * Constructor
	 */	
	public function TransformToolInternalControl(name : String, interactionMethod : Function = null, referenceName : String = null) 
	{
		this.name = name;
		this.interactionMethod = interactionMethod;
		this.referenceName = referenceName;
		addEventListener( TransformTool.CONTROL_INIT, init );
	}

	protected function init(event : Event) : void 
	{
		_transformTool.addEventListener( TransformTool.NEW_TARGET, draw );
		_transformTool.addEventListener( TransformTool.TRANSFORM_TOOL, draw );
		_transformTool.addEventListener( TransformTool.CONTROL_TRANSFORM_TOOL, position );
		_transformTool.addEventListener( TransformTool.CONTROL_PREFERENCE, draw );
		_transformTool.addEventListener( TransformTool.CONTROL_MOVE, controlMove );
		draw( );
	}

	public function draw(event : Event = null) : void 
	{
		if (_transformTool.maintainControlForm) 
		{
			counterTransform( );
		}
		position( );
	}

	public function position(event : Event = null) : void 
	{
		var reference : Point = referencePoint;
		if (reference) 
		{
			x = reference.x;
			y = reference.y;
		}
	}

	private function controlMove(event : Event) : void 
	{
		if (interactionMethod != null && _transformTool.currentControl == this) 
		{
			interactionMethod( );
		}
	}
}

class TransformToolMoveShape extends TransformToolInternalControl 
{

	private var lastTarget : DisplayObject;

	function TransformToolMoveShape(name : String, interactionMethod : Function) 
	{
		super( name, interactionMethod );
	}

	override public function draw(event : Event = null) : void 
	{
		
		var currTarget : DisplayObject;
		var moveUnderObjects : Boolean = _transformTool.moveUnderObjects;
		
		// use hitArea if moving under objects
		// then movement would have the same depth as the tool
		if (moveUnderObjects) 
		{
			hitArea = _transformTool.target as Sprite;
			currTarget = null;
			relatedObject = this;
		}else
		{
			
			// when not moving under objects
			// use the tool target to handle movement allowing
			// objects above it to be selectable
			hitArea = null;
			currTarget = DisplayObject(_transformTool.target);
			relatedObject = _transformTool.target as InteractiveObject;
		}
		
		if (lastTarget != currTarget) 
		{
			// set up/remove listeners for target being clicked
			if (lastTarget) 
			{
				lastTarget.removeEventListener( MouseEvent.MOUSE_DOWN, mouseDown, false );
			}
			if (currTarget) 
			{
				currTarget.addEventListener( MouseEvent.MOUSE_DOWN, mouseDown, false, 0, true );
			}
			
			// register/unregister cursor with the object
			var cursor : TransformToolCursor = _transformTool.moveCursor;
			cursor.removeReference( lastTarget );
			cursor.addReference( currTarget );
			
			lastTarget = currTarget;
		}
	}

	private function mouseDown(event : MouseEvent) : void 
	{
		dispatchEvent( new MouseEvent( MouseEvent.MOUSE_DOWN ) );
	}
}

class TransformToolRegistrationControl extends TransformToolInternalControl 
{

	function TransformToolRegistrationControl(name : String, interactionMethod : Function, referenceName : String) 
	{
		super( name, interactionMethod, referenceName );
	}

	override public function draw(event : Event = null) : void 
	{
		graphics.clear( );
		if (!_skin) 
		{
			graphics.lineStyle( 1, 0 );
			graphics.beginFill( 0xFFFFFF );
			graphics.drawCircle( 0, 0, _transformTool.controlSize / 2 );
			graphics.endFill( );
		}
		super.draw( );
	}
}

class TransformToolScaleControl extends TransformToolInternalControl 
{

	function TransformToolScaleControl(name : String, interactionMethod : Function, referenceName : String) 
	{
		super( name, interactionMethod, referenceName );
	}

	override public function draw(event : Event = null) : void 
	{
		graphics.clear( );
		if (!_skin) 
		{
			graphics.lineStyle( 2, 0xFFFFFF );
			graphics.beginFill( 0 );
			var size : Number = _transformTool.controlSize;
			var size2 : Number = size / 2;
			graphics.drawRect( -size2, -size2, size, size );
			graphics.endFill( );
		}
		super.draw( );
	}
}

class TransformToolRotateControl extends TransformToolInternalControl 
{

	private var locationName : String;

	function TransformToolRotateControl(name : String, interactionMethod : Function, locationName : String) 
	{
		super( name, interactionMethod );
		this.locationName = locationName;
	}

	override public function draw(event : Event = null) : void 
	{
		graphics.clear( );
		if (!_skin) 
		{
			graphics.beginFill( 0xFF, 0 );
			graphics.drawCircle( 0, 0, _transformTool.controlSize * 2 );
			graphics.endFill( );
		}
		super.draw( );
	}

	override public function position(event : Event = null) : void 
	{
		if (locationName in _transformTool) 
		{
			var location : Point = _transformTool[locationName];
			x = location.x;
			y = location.y;
		}
	}
}

class TransformToolSkewBar extends TransformToolInternalControl 
{

	private var locationStart : String;
	private var locationEnd : String;

	function TransformToolSkewBar(name : String, interactionMethod : Function, referenceName : String, locationStart : String, locationEnd : String) 
	{
		super( name, interactionMethod, referenceName );
		this.locationStart = locationStart;
		this.locationEnd = locationEnd;
	}

	override public function draw(event : Event = null) : void 
	{
		graphics.clear( );
		
		if (_skin) 
		{
			super.draw( event );
			return;
		}
		
		// derive point locations for bar
		var locStart : Point = _transformTool[locationStart];
		var locEnd : Point = _transformTool[locationEnd];
		// counter transform
		var toolTrans : Matrix;
		var toolTransInverted : Matrix;
		var maintainControlForm : Boolean = _transformTool.maintainControlForm;
		if (maintainControlForm) 
		{
			toolTrans = transform.concatenatedMatrix;
			toolTransInverted = toolTrans.clone( );
			toolTransInverted.invert( );
			
			locStart = toolTrans.transformPoint( locStart );
			locEnd = toolTrans.transformPoint( locEnd );
		}
		
		var size : Number = _transformTool.controlSize / 2;
		var diff : Point = locEnd.subtract( locStart );
		var angle : Number = Math.atan2( diff.y, diff.x ) - Math.PI / 2;	
		var offset : Point = Point.polar( size, angle );
		
		var corner1 : Point = locStart.add( offset );
		var corner2 : Point = locEnd.add( offset );
		var corner3 : Point = locEnd.subtract( offset );
		var corner4 : Point = locStart.subtract( offset );
		if (maintainControlForm) 
		{
			corner1 = toolTransInverted.transformPoint( corner1 );
			corner2 = toolTransInverted.transformPoint( corner2 );
			corner3 = toolTransInverted.transformPoint( corner3 );
			corner4 = toolTransInverted.transformPoint( corner4 );
		}
		
		// draw bar
		graphics.beginFill( 0xFF0000, 0 );
		graphics.moveTo( corner1.x, corner1.y );
		graphics.lineTo( corner2.x, corner2.y );
		graphics.lineTo( corner3.x, corner3.y );
		graphics.lineTo( corner4.x, corner4.y );
		graphics.lineTo( corner1.x, corner1.y );
		graphics.endFill( );
	}

	override public function position(event : Event = null) : void 
	{
		if (_skin) 
		{
			var locStart : Point = _transformTool[locationStart];
			var locEnd : Point = _transformTool[locationEnd];
			var location : Point = Point.interpolate( locStart, locEnd, .5 );
			x = location.x;
			y = location.y;
		}else
		{
			x = 0;
			y = 0;
			draw( event );
		}
	}
}

class TransformToolOutline extends TransformToolInternalControl 
{

	function TransformToolOutline(name : String) 
	{
		super( name );
	}

	override public function draw(event : Event = null) : void 
	{
		var topLeft : Point = _transformTool.boundsTopLeft;
		var topRight : Point = _transformTool.boundsTopRight;
		var bottomRight : Point = _transformTool.boundsBottomRight;
		var bottomLeft : Point = _transformTool.boundsBottomLeft;
		
		graphics.clear( );
		graphics.lineStyle( 0, 0 );
		graphics.moveTo( topLeft.x, topLeft.y );
		graphics.lineTo( topRight.x, topRight.y );
		graphics.lineTo( bottomRight.x, bottomRight.y );
		graphics.lineTo( bottomLeft.x, bottomLeft.y );
		graphics.lineTo( topLeft.x, topLeft.y );
	}

	override public function position(event : Event = null) : void 
	{
		draw( event );
	}
}

// Cursors

class TransformToolInternalCursor extends TransformToolCursor 
{

	public var offset : Point = new Point( );
	public var icon : Shape = new Shape( );

	public function TransformToolInternalCursor() 
	{
		addChild( icon );
		offset = _mouseOffset;
		addEventListener( TransformTool.CONTROL_INIT, init );
	}

	private function init(event : Event) : void 
	{
		_transformTool.addEventListener( TransformTool.NEW_TARGET, maintainTransform );
		_transformTool.addEventListener( TransformTool.CONTROL_PREFERENCE, maintainTransform );
		draw( );
	}

	protected function maintainTransform(event : Event) : void 
	{
		offset = _mouseOffset;
		if (_transformTool.maintainControlForm) 
		{
			transform.matrix = new Matrix( );
			var concatMatrix : Matrix = transform.concatenatedMatrix;
			concatMatrix.invert( );
			transform.matrix = concatMatrix;
			offset = concatMatrix.deltaTransformPoint( offset );
		}
		updateVisible( event );
	}

	protected function drawArc(originX : Number, originY : Number, radius : Number, angle1 : Number, angle2 : Number, useMove : Boolean = true) : void 
	{
		var diff : Number = angle2 - angle1;
		var divs : Number = 1 + Math.floor( Math.abs( diff ) / (Math.PI / 4) );
		var span : Number = diff / (2 * divs);
		var cosSpan : Number = Math.cos( span );
		var radiusc : Number = cosSpan ? radius / cosSpan : 0;
		var i : int;
		if (useMove) 
		{
			icon.graphics.moveTo( originX + Math.cos( angle1 ) * radius, originY - Math.sin( angle1 ) * radius );
		}else
		{
			icon.graphics.lineTo( originX + Math.cos( angle1 ) * radius, originY - Math.sin( angle1 ) * radius );
		}
		for (i = 0; i < divs ; i++) 
		{
			angle2 = angle1 + span;
			angle1 = angle2 + span;
			icon.graphics.curveTo( originX + Math.cos( angle2 ) * radiusc, originY - Math.sin( angle2 ) * radiusc, originX + Math.cos( angle1 ) * radius, originY - Math.sin( angle1 ) * radius );
		}
	}

	protected function getGlobalAngle(vector : Point) : Number 
	{
		var globalMatrix : Matrix = _transformTool.globalMatrix;
		vector = globalMatrix.deltaTransformPoint( vector );
		return Math.atan2( vector.y, vector.x ) * (180 / Math.PI);
	}

	override public function position(event : Event = null) : void 
	{
		if (parent) 
		{
			x = parent.mouseX + offset.x;
			y = parent.mouseY + offset.y;
		}
	}

	public function draw() : void 
	{
		icon.graphics.beginFill( 0 );
		icon.graphics.lineStyle( 1, 0xFFFFFF );
	}
}

class TransformToolRegistrationCursor extends TransformToolInternalCursor 
{

	public function TransformToolRegistrationCursor() 
	{
	}

	override public function draw() : void 
	{
		super.draw( );
		icon.graphics.drawCircle( 0, 0, 2 );
		icon.graphics.drawCircle( 0, 0, 4 );
		icon.graphics.endFill( );
	}
}

class TransformToolMoveCursor extends TransformToolInternalCursor 
{

	public function TransformToolMoveCursor() 
	{
	}

	override public function draw() : void 
	{
		super.draw( );
		// up arrow
		icon.graphics.moveTo( 1, 1 );
		icon.graphics.lineTo( 1, -2 );
		icon.graphics.lineTo( -1, -2 );
		icon.graphics.lineTo( 2, -6 );
		icon.graphics.lineTo( 5, -2 );
		icon.graphics.lineTo( 3, -2 );
		icon.graphics.lineTo( 3, 1 );
		// right arrow
		icon.graphics.lineTo( 6, 1 );
		icon.graphics.lineTo( 6, -1 );
		icon.graphics.lineTo( 10, 2 );
		icon.graphics.lineTo( 6, 5 );
		icon.graphics.lineTo( 6, 3 );
		icon.graphics.lineTo( 3, 3 );
		// down arrow
		icon.graphics.lineTo( 3, 5 );
		icon.graphics.lineTo( 3, 6 );
		icon.graphics.lineTo( 5, 6 );
		icon.graphics.lineTo( 2, 10 );
		icon.graphics.lineTo( -1, 6 );
		icon.graphics.lineTo( 1, 6 );
		icon.graphics.lineTo( 1, 5 );
		// left arrow
		icon.graphics.lineTo( 1, 3 );
		icon.graphics.lineTo( -2, 3 );
		icon.graphics.lineTo( -2, 5 );
		icon.graphics.lineTo( -6, 2 );
		icon.graphics.lineTo( -2, -1 );
		icon.graphics.lineTo( -2, 1 );
		icon.graphics.lineTo( 1, 1 );
		icon.graphics.endFill( );
	}
}

class TransformToolScaleCursor extends TransformToolInternalCursor 
{

	public function TransformToolScaleCursor() 
	{
	}

	override public function draw() : void 
	{
		super.draw( );
		// right arrow
		icon.graphics.moveTo( 4.5, -0.5 );
		icon.graphics.lineTo( 4.5, -2.5 );
		icon.graphics.lineTo( 8.5, 0.5 );
		icon.graphics.lineTo( 4.5, 3.5 );
		icon.graphics.lineTo( 4.5, 1.5 );
		icon.graphics.lineTo( -0.5, 1.5 );
		// left arrow
		icon.graphics.lineTo( -3.5, 1.5 );
		icon.graphics.lineTo( -3.5, 3.5 );
		icon.graphics.lineTo( -7.5, 0.5 );
		icon.graphics.lineTo( -3.5, -2.5 );
		icon.graphics.lineTo( -3.5, -0.5 );
		icon.graphics.lineTo( 4.5, -0.5 );
		icon.graphics.endFill( );
	}

	override public function updateVisible(event : Event = null) : void 
	{
		super.updateVisible( event );
		if (event) 
		{
			var reference : TransformToolScaleControl = event.target as TransformToolScaleControl;
			if (reference) 
			{
				switch(reference) 
				{
					case _transformTool.scaleTopLeftControl:
					case _transformTool.scaleBottomRightControl:
						icon.rotation = (getGlobalAngle( new Point( 0, 100 ) ) + getGlobalAngle( new Point( 100, 0 ) )) / 2;
						break;
					case _transformTool.scaleTopRightControl:
					case _transformTool.scaleBottomLeftControl:
						icon.rotation = (getGlobalAngle( new Point( 0, -100 ) ) + getGlobalAngle( new Point( 100, 0 ) )) / 2;
						break;
					case _transformTool.scaleTopControl:
					case _transformTool.scaleBottomControl:
						icon.rotation = getGlobalAngle( new Point( 0, 100 ) );
						break;
					default:
						icon.rotation = getGlobalAngle( new Point( 100, 0 ) );
				}
			}
		}
	}
}

class TransformToolRotateCursor extends TransformToolInternalCursor 
{

	public function TransformToolRotateCursor() 
	{
	}

	override public function draw() : void 
	{
		super.draw( );
		// curve
		var angle1 : Number = Math.PI;
		var angle2 : Number = -Math.PI / 2;
		drawArc( 0, 0, 4, angle1, angle2 );
		drawArc( 0, 0, 6, angle2, angle1, false );
		// arrow
		icon.graphics.lineTo( -8, 0 );
		icon.graphics.lineTo( -5, 4 );
		icon.graphics.lineTo( -2, 0 );
		icon.graphics.lineTo( -4, 0 );
		icon.graphics.endFill( );
	}
}

class TransformToolSkewCursor extends TransformToolInternalCursor 
{

	public function TransformToolSkewCursor() 
	{
	}

	override public function draw() : void 
	{
		super.draw( );
		// right arrow
		icon.graphics.moveTo( -6, -1 );
		icon.graphics.lineTo( 6, -1 );
		icon.graphics.lineTo( 6, -4 );
		icon.graphics.lineTo( 10, 1 );
		icon.graphics.lineTo( -6, 1 );
		icon.graphics.lineTo( -6, -1 );
		icon.graphics.endFill( );
		
		super.draw( );
		// left arrow
		icon.graphics.moveTo( 10, 5 );
		icon.graphics.lineTo( -2, 5 );
		icon.graphics.lineTo( -2, 8 );
		icon.graphics.lineTo( -6, 3 );
		icon.graphics.lineTo( 10, 3 );
		icon.graphics.lineTo( 10, 5 );
		icon.graphics.endFill( );
	}

	override public function updateVisible(event : Event = null) : void 
	{
		super.updateVisible( event );
		if (event) 
		{
			var reference : TransformToolSkewBar = event.target as TransformToolSkewBar;
			if (reference) 
			{
				switch(reference) 
				{
					case _transformTool.skewLeftControl:
					case _transformTool.skewRightControl:
						icon.rotation = getGlobalAngle( new Point( 0, 100 ) );
						break;
					default:
						icon.rotation = getGlobalAngle( new Point( 100, 0 ) );
				}
			}
		}
	}
}
